// reactive.js: Vue3 响应式系统的简化实现（ES6 模块化）

// --------------------
// 全局变量和数据结构
// --------------------

// activeEffect 用来存储当前正在执行的副作用函数（effect），它会被 track 函数用来收集依赖
let activeEffect = null;

// targetMap 是一个 WeakMap，用来存储每个被代理对象与其属性依赖关系的映射
// key: 被代理的目标对象
// value: Map 对象，存储 target 对象的各个属性与其对应的依赖集合（Set），例如：{ 'count' => Set(effect1, effect2) }
const targetMap = new WeakMap();

// --------------------
// 工具函数：响应式标记相关
// --------------------

/**
 * 判断对象是否已经是响应式对象
 * @param {Object} obj - 待检查的对象
 * @returns {boolean} 如果对象已被代理为响应式对象，则返回 true
 */
export function isReactive(obj) {
	return obj && obj.__v_isReactive === true;
}

// --------------------
// 核心 API：创建响应式对象 reactive
// --------------------

/**
 * @param {Object} target - 需要被代理的目标对象
 * @returns {Proxy} 返回一个响应式代理对象
 */
export function reactive(target) {
	// 如果目标已经是响应式对象，则直接返回
	if (isReactive(target)) {
		return target;
	}

	// 创建代理对象，拦截 get 和 set 操作
	const proxy = new Proxy(target, {
		/**
		 * get 拦截器，用于读取属性值时进行依赖收集
		 * @param {Object} obj - 被代理的原始对象
		 * @param {string} key - 正在访问的属性名
		 * @param {Proxy} receiver - 代理对象本身（可能用于继承场景）
		 * @returns {*} 返回属性值
		 */
		get(obj, key, receiver) {
			// 调用 track() 收集依赖，将当前激活的副作用函数与 obj[key] 建立关系
			track(obj, key);
			// 使用 Reflect.get() 获取原始值，保证 this 指向正确
			// 如果直接使用 obj[key]，无法 track 
			// 如果使用 receiver[key]，则会导致 get track → 无限递归。
			return Reflect.get(obj, key, receiver);
		},
		/**
		 * set 拦截器，用于设置属性值时触发依赖更新
		 * @param {Object} obj - 被代理的原始对象
		 * @param {string} key - 正在设置的属性名
		 * @param {*} value - 新的属性值
		 * @param {Proxy} receiver - 代理对象本身
		 * @returns {boolean} 返回操作是否成功
		 */
		set(obj, key, value, receiver) {
			// 使用 Reflect.set() 更新属性值
			const result = Reflect.set(obj, key, value, receiver);
			// 调用 trigger() 通知所有依赖于该属性的副作用函数更新
			trigger(obj, key);
			return result;
		}
	});

	// 给代理对象添加标记，避免被重复代理
	proxy.__v_isReactive = true;
	return proxy;
}

// --------------------
// 核心 API：副作用函数 effect
// --------------------

/**
 * 注册并执行副作用函数，使其成为响应式依赖收集的目标
 * 当内部依赖的数据发生变化时，副作用函数会重新执行
 *
 * @param {Function} fn - 传入的副作用函数，该函数内部会访问响应式数据
 */
export function effect(fn) {
	// 定义一个 effectFn 函数封装实际的副作用逻辑
	const effectFn = () => {
		// 设置全局的 activeEffect 为当前的 effectFn，便于 track 收集依赖
		activeEffect = effectFn;
		// 执行传入的副作用函数 fn，此过程中所有访问的响应式数据都会调用 track() 收集依赖
		fn();
		// 函数执行完毕后，重置 activeEffect 为 null，避免误收集其他非关联依赖
		activeEffect = null;
	};
	// 立即执行 effectFn 以便初始化依赖收集
	effectFn();
}

// --------------------
// 内部函数：依赖收集 track
// --------------------

/**
 * 依赖收集函数：将当前激活的副作用函数（activeEffect）与目标对象的属性关联
 * 当目标对象的该属性发生变化时，会重新调用关联的副作用函数
 *
 * @param {Object} target - 目标对象（被代理的对象）
 * @param {string} key - 属性名，即正在访问的属性
 */
function track(target, key) {
	// 如果当前没有活跃的副作用函数，则无需收集依赖，直接返回
	if (!activeEffect) return;

	// 从全局 targetMap 中获取目标对象对应的 depsMap（依赖映射表）
	let depsMap = targetMap.get(target);
	if (!depsMap) {
		// 如果目标对象还没有依赖映射，则创建一个新的 Map，并将其存入 targetMap
		depsMap = new Map();
		targetMap.set(target, depsMap);
	}

	// 从 depsMap 中获取对应属性 key 的依赖集合（Set）
	let deps = depsMap.get(key);
	if (!deps) {
		// 如果该属性尚未建立依赖集合，则创建一个新的 Set，并存入 depsMap
		deps = new Set();
		depsMap.set(key, deps);
	}

	// 将当前激活的副作用函数添加到该属性的依赖集合中
	deps.add(activeEffect);
}

// --------------------
// 内部函数：依赖触发 trigger
// --------------------

/**
 * 依赖触发函数：当目标对象的属性值改变时，通知所有依赖该属性的副作用函数重新执行
 *
 * @param {Object} target - 目标对象
 * @param {string} key - 发生变化的属性名
 */
function trigger(target, key) {
	// 从 targetMap 中获取目标对象的依赖映射表
	const depsMap = targetMap.get(target);
	if (!depsMap) return; // 如果没有依赖映射，则无需触发任何副作用

	// 获取属性 key 对应的依赖集合
	const deps = depsMap.get(key);
	if (deps) {
		// 遍历依赖集合，依次执行每个副作用函数，触发更新
		deps.forEach(effectFn => effectFn());
	}
}

